﻿
/*
 * PsTFS is a set of PowerShell commands to manage and use Team Foundation Server. PowerShell 
 * effectiveness is combined with the power of TFS API to maximizes TFS capabilities.
 * 
 * Creator : Choulant Noham
 * Mail : choulant@gmail.com
 * Blog : http://choulant.blogspot.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation;

namespace PsTFS.Methodes
{
    public enum TraverseType
    {
        PreOrder,   // Returns the current node before any children nodes
        PostOrder   // Returns any children nodes before the current node
    }

    /// <summary>
    /// Extention de méthode
    /// </summary>
    public static class MethodExtention
    {
        /// <summary>
        /// Ajout de la méthode sur le workitem pour avoir facilement tous les changesets
        /// </summary>
        /// <param name="wi"></param>
        /// <returns></returns>
        public static List<Changeset> ListChangeset(this WorkItem wi) 
        {  
            var vcs = MethodService.VersionControlServer();
            var associatedChangesets = new List<Changeset>();
            
            // Suppressing resharper suggestion, as the resulting LINQ is less understandable
            // ReSharper disable LoopCanBeConvertedToQuery
            foreach (Link link in wi.Links)
            {
                var extLink = link as ExternalLink;
                if (extLink != null)
                {
                    var artifact = LinkingUtilities.DecodeUri(extLink.LinkedArtifactUri);
                    if (String.Equals(artifact.ArtifactType, "Changeset", StringComparison.Ordinal))
                    {
                        // Convert the artifact URI to Changeset object.
                        associatedChangesets.Add(vcs.ArtifactProvider.GetChangeset(new Uri(extLink.LinkedArtifactUri)));
                    }
                }
            }
            // ReSharper restore LoopCanBeConvertedToQuery

            return associatedChangesets;
        }

        /// <summary>
        /// Method to traverse a Hierarchical data structure with LINQ
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">source object</param>
        /// <param name="childrenSelector"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        /// <see cref="http://mjuraszek.blogspot.com/2013/08/querying-hierarchical-data-using-linq.html" />
        public static IEnumerable<T> Traverse<T>(this T source, Func<T, IEnumerable<T>> childrenSelector,
                                                 TraverseType type)
        {
            if (childrenSelector == null)
            {
                throw new ArgumentNullException("childrenSelector");
            }

            IEnumerable<T> result;
            switch (type)
            {
                case TraverseType.PostOrder:
                    result = TraversePostOrder(source, childrenSelector);
                    break;
                case TraverseType.PreOrder:
                    result = TraversePreOrder(source, childrenSelector);
                    break;
                default:
                    throw new ArgumentException("Unknow TraverseType specified.", "type");
            }
            return result;
        }

        private static IEnumerable<T> TraversePreOrder<T>(T source, Func<T, IEnumerable<T>> childrenSelector)
        {
            // return current node itself
            yield return source;
            // run TraversePreOrder on children collection
            foreach (var child in TraversePreOrder(childrenSelector(source), childrenSelector))
                yield return child;
        }

        private static IEnumerable<T> TraversePreOrder<T>(IEnumerable<T> source,
                                                          Func<T, IEnumerable<T>> childrenSelector)
        {
            // check if collection is null to avoid ArgumentNullException thrown from SelectMany
            if (source == null)
                yield break;

            // call TraversePreOrder on every collection item
            foreach (T child in source.SelectMany(c => TraversePreOrder(c, childrenSelector)))
                yield return child;
        }

        private static IEnumerable<T> TraversePostOrder<T>(T source, Func<T, IEnumerable<T>> childrenSelector)
        {
            // run TraversePostOrder on children collection
            foreach (T child in TraversePostOrder(childrenSelector(source), childrenSelector))
                yield return child;

            // return current node itself
            yield return source;
        }

        private static IEnumerable<T> TraversePostOrder<T>(IEnumerable<T> source,
                                                           Func<T, IEnumerable<T>> childrenSelector)
        {
            // check if collection is null to avoid ArgumentNullException thrown from SelectMany
            if (source == null)
                yield break;

            // call TraversePostOrder on every collection item
            foreach (T child in source.SelectMany(c => TraversePostOrder(c, childrenSelector)))
                yield return child;
        }
    }
}
